package com.zxd.interview.consistenthash;

import com.zxd.interview.util.MapUtils;
import org.apache.commons.codec.digest.DigestUtils;

import java.util.*;

/**
 * 使用TreeMap 实现一致性哈希
 *
 * @author Xander
 * @version v1.0.0
 * @Package : consistenthash
 * @Description : 使用TreeMap 实现一致性哈希
 * @Create on : 2023/7/3 11:15
 **/
public class ConsistantHashAccording2TreeMap {

    /**
     * 虚拟节点数量
     */
    private static final int VIRTUAL_NODE_SIZE = 4;

    /**
     * Key 为 Long 的原因是服务器IP实际是32位整数。
     */
    private final TreeMap<Long, String> treeMap = new TreeMap<>();

    /**
     * 在服务器列表中根据key定位节点
     *
     * @param values
     * @return java.lang.String
     * @description 在服务器列表中根据key定位节点
     * @param: key
     * @author xander
     * @date 2023/7/3 11:25
     */
    public String choose(List<String> values, String key) {
        for (String value : values) {
            //value作为key，hash值作为value
            add(hash(value), value);
        }
        return selectNode(key);
    }

    /**
     * hash落环，并加入虚拟节点
     *
     * @param key
     * @return void
     * @description `hash落环，并加入虚拟节点
     * @param: value
     * @author xander
     * @date 2023/7/3 11:25
     */
    private void add(long key, String value) {
        treeMap.clear();
        //虚拟节点
        for (int i = 0; i < VIRTUAL_NODE_SIZE; i++) {
            Long hash = this.hash("vir" + key + i);
            treeMap.put(hash, value);
        }
        treeMap.put(key, value);
    }

    /**
     * 在环中根据传入的值找到第一个server，使用tailMap特性模拟hash环，如果tailMap返回空，则表示已经到了hash环的末尾，那么需要使用第一个key
     *
     * @param value
     * @return java.lang.String
     * @description 在环中根据传入的值找到第一个server，使用tailMap特性模拟hash环，如果tailMap返回空，则表示已经到了hash环的末尾，那么需要使用第一个key
     * @author xander
     * @date 2023/7/3 11:25
     */
    private String selectNode(String value) {
        long hash = hash(value);
        SortedMap<Long, String> after = treeMap.tailMap(hash);
        if (MapUtils.isNotEmpty(after)) {
            String server = after.get(after.firstKey());
            System.out.println("路由成功：value: " + value + ", route server: " + server);
            return server;
        }
        return treeMap.firstEntry().getValue();
    }

    /**
     * hash计算
     * @description hash计算
     * @param value
     * @return java.lang.Long
     * @author xander
     * @date 2023/7/3 11:25
     */
    private Long hash(String value) {
        // md5 加密转为byte[]
        byte[] digest = DigestUtils.md5(value);

        // hash code, Truncate to 32-bits
        long hashCode = ((long) (digest[3] & 0xFF) << 24) | ((long) (digest[2] & 0xFF) << 16) | ((long) (digest[1] & 0xFF) << 8) | (digest[0] & 0xFF);

        return hashCode & 0xffffffffL;
    }

    public static void main(String[] args) {
        ConsistantHashAccording2TreeMap consistantHashAccording2TreeMap = new ConsistantHashAccording2TreeMap();

        consistantHashAccording2TreeMap.choose(Arrays.asList("111"), "11");
    }/*具体使用：

    public class ConsistentHashRouter implements Router {

    private TreeMapConsistentHash hash = new TreeMapConsistentHash();


     * values: List<String> servers = Lists.newArrayList("127.0.0.1","192.168.11.1" ...)
     * key: consumer的特定数值，比如根据userId

//    @Override
//    public String chooseServer(List<String> values, String key) {
//        return hash.choose(values,key);
//    }
//}
    */
}
